package system

import (
	"context"
	"database/sql"
	"encoding/json"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gtime"
	"server/internal/consts"
	"server/internal/dao"
	"server/internal/model"
	"server/internal/model/do"
	"server/internal/model/entity"
	"server/internal/service"
)

type sSystemAuthority struct {
}

func init() {
	service.RegisterSystemAuthority(&sSystemAuthority{})
}

func (s *sSystemAuthority) RoleBindPermission(ctx context.Context, roleId int64, permissionIds []int64) (err error) {
	err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		adminID := service.SystemLogin().GetAdminID(ctx)

		//标记删除
		_, err = tx.Ctx(ctx).Update(dao.SystemRolePermission.Table(), do.SystemRolePermission{
			DeletedBy: adminID,
			DeletedAt: gtime.Now(),
		}, do.SystemRolePermission{
			RoleId: roleId,
		})
		if err != nil {
			return err
		}

		//添加最新的绑定数据
		if len(permissionIds) > 0 {
			data := make([]do.SystemRolePermission, 0, len(permissionIds))
			for _, permissionId := range permissionIds {
				data = append(data, do.SystemRolePermission{
					RoleId:       roleId,
					PermissionId: permissionId,
					CreatedBy:    adminID,
				})
			}
			if _, err = tx.Ctx(ctx).Insert(dao.SystemRolePermission.Table(), data); err != nil {
				return err
			}
		}
		return nil
	})
	if err == nil {
		service.SystemAuthorityCache().SetSystemPermissionLastUpdatedTime(ctx)
	}
	return err
}

// GetUserAuthData 读取用户权限数据
func (s *sSystemAuthority) GetUserAuthData(ctx context.Context, userId int64) (out *model.SystemUserAuthData, err error) {
	out = new(model.SystemUserAuthData)
	adminInfo := new(entity.SystemUser)
	err = dao.SystemUser.Ctx(ctx).Where(do.SystemUser{Id: userId}).Scan(adminInfo)
	if err != nil {
		if gerror.Is(err, sql.ErrNoRows) {
			return out, nil
		}
		return nil, err
	}

	out.Id = int64(adminInfo.Id)
	out.Name = adminInfo.Name
	out.Username = adminInfo.Username
	out.Avatar = adminInfo.Avatar
	out.IsSuper = adminInfo.IsSuper
	out.Phone = adminInfo.Phone
	out.RememberToken = adminInfo.RememberToken

	//查询用户权限数据
	permissions, err := s.GetUserPermission(ctx, adminInfo)
	for _, permission := range permissions {
		//解析权限中的routes
		permissionRoutes := make([]string, 0)
		_ = json.Unmarshal([]byte(permission.Routes), &permissionRoutes)
		if len(permissionRoutes) > 0 {
			out.Routes = append(out.Routes, permissionRoutes...)
		}

		//解析权限CODE所属类型
		switch permission.Type {
		case consts.PermissionTypeMenu:
			out.MenuCodes = append(out.MenuCodes, permission.Code)
		case consts.PermissionTypeAction:
			out.PermissionCodes = append(out.PermissionCodes, permission.Code)
		}

	}

	return out, err
}

// GetUserPermission 获取用户权限信息
func (s *sSystemAuthority) GetUserPermission(ctx context.Context, adminInfo *entity.SystemUser) (permissions []entity.SystemPermission, err error) {
	permissions = make([]entity.SystemPermission, 0)

	//超管查询所有的数据
	if adminInfo.IsSuper == consts.IsSuper {
		err = dao.SystemPermission.Ctx(ctx).Scan(&permissions)
		return permissions, err
	}

	err = dao.SystemPermission.Ctx(ctx).As("p").
		InnerJoin("system_role_permission rp", "rp.permission_id = p.id").
		InnerJoin("system_role_user ru", "ru.role_id = rp.role_id").
		InnerJoin("system_role r", "r.id = ru.role_id").
		Fields("p.*").
		Where(g.Map{
			"ru.user_id": adminInfo.Id,
		}).
		Scan(&permissions)
	if err != nil {
		return
	}
	return permissions, nil
}

// HasPermissionRoute 检测路由地址是否在权限范围内
func (s *sSystemAuthority) HasPermissionRoute(ctx context.Context, route string) bool {
	authData, _ := service.SystemAuthorityCache().GetUserCacheAuthData(ctx)
	if authData.Id == 0 {
		return false
	}
	if authData.IsSuper == consts.IsSuper {
		return true
	}
	for _, item := range authData.Routes {
		if item == route {
			return true
		}
	}
	return false
}

// HasPermissionCode 根据权限CODE检测登录用户是否有权限
func (s *sSystemAuthority) HasPermissionCode(ctx context.Context, code string) bool {
	authData, _ := service.SystemAuthorityCache().GetUserCacheAuthData(ctx)
	if authData.Id == 0 {
		return false
	}
	if authData.IsSuper == consts.IsSuper {
		return true
	}
	for _, item := range authData.PermissionCodes {
		if item == code {
			return true
		}
	}
	return false
}
